////////////////////////////////////////////////////////////////////////
// Inicjalizacja danych
////////////////////////////////////////////////////////////////////////

%{
	/**********************************************************************
	 * Katarzyna Ciechańska, v.2.0.0
	 * 
	 * Plik z regułami dla parsera GNU Bison, wersja >= 2.3.
	 *********************************************************************/
	 
	#include <boost/algorithm/string.hpp>
	#include "../mime/data/MimeDefaults.h"
	#include "FlexScanner.h"

    /* połączenie z obiektem skanera */
	#undef  yylex
	#define yylex			scanner->scan       // scanner to parametr podawany bisonowi
	#define yy_entity_start	scanner->fs_last_entity_start

    /* pomocnicze nazwy */
	#define yy_tokens	scanner->fs_tokens			// lista tokenów przechowywana przez skaner
	#define yy_end		scanner->fs_tokens.end()			// i jej koniec
	#define yy_last		scanner->lastToken()				// i jej ostatnio rozpoznany element

	using namespace kc_sf;

	//#define DEBUG
%}

%code requires {
	#include "Yystype.h"
	#include "FlexScanner.h"
	#include "../mime/data/Entity.h"
}

	/* opcje parsera */
%require    "2.3"                               // wersja programu bison
%error-verbose                                  // bardziej czytelne bledy
%skeleton   "lalr1.cc"                          // analiza wstępująca z podglądem 1 elementu
%define     "parser_class_name" "BisonParser"   // nazwa klasy generowanej przez bisona

	/* dodatkowe parametry przekazywane bisonowi */
%parse-param    { FlexScanner *scanner }        // klasa skanera
%parse-param    { Entity *top_entity }       	// główna encja wiadomości 
%parse-param    { Entity *act_entity }       	// aktulnie przetwarzana encja 

////////////////////////////////////////////////////////////////////////
//  Tokeny 
////////////////////////////////////////////////////////////////////////

%right <yTokenIterator>	WSP CRLF EOH
                        QUOTED_PAIR QTEXT NUMBER DAY MONTH
                        CTEXT TEXT DTEXT ENCODED_WORD UNSTRUCTURED
%right <yTokenIterator>	LB LSQB LTRB LDQUOTE SIGN OBS_ZONE EQUAL SLASH
                        RB RSQB RTRB RDQUOTE DOT AT COLON SEMICOLON COMMA
%right <yTokenIterator>	H_DATE H_FROM H_SENDER H_REPLY_TO H_TO H_CC 
			H_BCC H_MID 
                        H_IN_REPLY_TO H_REFERENCES H_SUBJECT H_COMMENTS
                        H_KEYWORDS H_PATH H_RECEIVED
                        H_VERSION H_CONTENT_TYPE H_TRANSFER_ENC
                        H_DESCRIPTION H_CID H_UNKNOWN
%right <yTokenIterator>	BOUNDARY BOUNDARY_END 
                        ATEXT TOKEN MULTIPART

    /* iterator do kolekcji tokenów */
%type <yTokenIterator>	// encje
			singlepart multipart
			delimiter_entity entity program
                        delimiter close_delimiter multipart_body
                        // podstawowe elementy  
                        cfws commas dots fws wsp crlf
                        opt_cfws opt_commas opt_crlfs opt_fws 
                        dot_atom atom atextseq
                        // komentarze 
                        ccontent ccontent_seq ctextseq comment 
                        // struktura nagłówków                          
			headers entity_header eoh
			header field 
			content_description content_id 
                        item_name name_val_pair name_val_list 
                        unstructured_elem unstructured
                        zone 
                        obs_route 
                        obs_domain_list obs_domain_name 
                        received received_seq 
			version
			parameters parameter
			time date_time
                        content_transfer_encoding 
                        content_type multipart_ct
                        bcc cc comments date from in_reply_to 
                        keywords message_id references reply 
                        sender subject to unknown 
                        trace
			localpart domain domain_literal
                        dcontent dcontent_seq dtextseq 
                        // cytaty 
                        qcontent qcontent_seq qtextseq quoted_string 
                        phrase phrase_list obs_phrase_list 
                        text textseq
                        // body
                        singlepart_body
    /* pojedynczy adres o formie: localpart@domain */
%type <yAddress>     	addr_spec angle_addr mailbox msg_id name_addr 
                        path 
    /* grupa adresów złożona z pojedynczych adresów */
%type <yAddressCollection>     
			msg_id_list msg_list obs_msg_list  
                        mailbox_list mlist obs_mlist 
			address_list alist obs_alist address group
			angle_addr_list
 
%start program

%%

////////////////////////////////////////////////////////////////////////
//  Struktura wiadomości 
////////////////////////////////////////////////////////////////////////

program: 
    opt_cfws entity    {
		if( act_entity != top_entity )
			YYABORT;
        top_entity->eSize = scanner->fs_bytes;
    }
;

entity:
    singlepart
    | multipart
;

singlepart:
    singlepart_headers eoh singlepart_body
    | eoh singlepart_body
;

multipart:
    multipart_headers eoh multipart_body {
	#ifdef DEBUG
	std::cout << "<- Multipart" << std::endl;
	#endif // DEBUG
}
;

eoh: 
    EOH opt_crlfs {
	#ifdef DEBUG
	std::cout << "EOH" << std::endl;
	#endif // DEBUG
}
;

singlepart_headers:
    content_type
    | content_type headers
    | headers content_type
    | headers content_type headers
;

multipart_headers:
    multipart_ct
    | multipart_ct headers
    | headers multipart_ct
    | headers multipart_ct headers
;

headers:     
    headers header	// EOL
    | header	// EOL
;  

header:    
    entity_header   // moze == NULL
    | field
    | version
;

// Nagłówki RFC 2822 ------------------------------------------------ //

field:
    date
    | from
    | sender
    | reply
    | to
    | cc
    | bcc
    | message_id
    | in_reply_to
    | references
    | keywords
    | trace
    | subject
    | comments
    | unknown
;

date:
    H_DATE opt_cfws COLON opt_cfws date_time    {
		TokenPtr tmp = yy_end;						
		act_entity->addHeader( 
			new Header( $1->value(), TokenCollection( $5, --tmp ).toStr() ) );
    }
;

from:
    H_FROM opt_cfws COLON opt_cfws mailbox_list    { // moze == NULL
		TokenPtr tmp = yy_end;						
		act_entity->addHeader( 
			new Header( $1->value(), TokenCollection( ++ $4, --tmp ).toStr() ) );
		act_entity->eFrom = $5[0];
    }
;

sender:
    H_SENDER opt_cfws COLON opt_cfws mailbox    { // moze == NULL
		act_entity->addHeader( new Header( $1->value(), $5.toString() ) );
    }
;

reply:
    H_REPLY_TO opt_cfws COLON opt_cfws address_list    { // moze == NULL
		TokenPtr tmp = yy_end;						
		act_entity->addHeader( 
			new Header( $1->value(), TokenCollection( ++ $4, --tmp ).toStr() ) );
    }
;

to:
    H_TO opt_cfws COLON opt_cfws address_list    { // moze == NULL
		TokenPtr tmp = yy_end;						
		act_entity->addHeader( 
			new Header( $1->value(), TokenCollection( ++ $4, --tmp ).toStr() ) );
		act_entity->eTo = $5;
    }
;

cc:
    H_CC opt_cfws COLON opt_cfws address_list    { // moze == NULL
		TokenPtr tmp = yy_end;						
		act_entity->addHeader( 
			new Header( $1->value(), TokenCollection( ++ $4, --tmp ).toStr() ) );
		act_entity->eCc = $5;
    }
;

bcc:
    H_BCC opt_cfws COLON opt_cfws address_list    {
		TokenPtr tmp = yy_end;						
		act_entity->addHeader( 
			new Header( $1->value(), TokenCollection( ++ $4, --tmp ).toStr() ) );
		act_entity->eBcc = $5;
    }
;

message_id:
    H_MID opt_cfws COLON opt_cfws msg_id    {
		act_entity->addHeader( new Header( $1->value(), $5.toString() ) );
    }
;

in_reply_to:
    H_IN_REPLY_TO opt_cfws COLON opt_cfws msg_id_list    {
		TokenPtr tmp = yy_end;						
		act_entity->addHeader( 
			new Header( $1->value(), TokenCollection( ++ $4, --tmp ).toStr() ) );
    }
;

references:
    H_REFERENCES opt_cfws COLON opt_cfws msg_id_list    {
		TokenPtr tmp = yy_end;						
		act_entity->addHeader( 
			new Header( $1->value(), TokenCollection( ++ $4, --tmp ).toStr() ) );
    }
;

keywords:
    H_KEYWORDS opt_cfws COLON opt_cfws phrase_list    {
		TokenPtr tmp = yy_end;						
		act_entity->addHeader( 
			new Header( $1->value(), TokenCollection( $5, --tmp ).toStr() ) );
    }
;

subject:
    H_SUBJECT opt_cfws COLON opt_cfws unstructured    {	// unstructured moze == NULL
		Header *header = new Header( $1->value(), TokenCollection( $5, yy_last ).toStr() );

 		act_entity->addHeader( header );
		act_entity->eSubject = header->getValue();
   }
;

comments:
    H_COMMENTS opt_cfws COLON opt_cfws unstructured    {     	// unstructured moze == NULL           
		TokenPtr tmp = yy_end;						
 		act_entity->addHeader( 
			new Header( $1->value(), TokenCollection( $5, --tmp ).toStr() ) );
    }
;

unknown:
    H_UNKNOWN opt_cfws COLON opt_cfws unstructured    {	// unstructured moze == NULL
		TokenPtr tmp = yy_end;						
 		act_entity->addHeader( 
			new Header( $1->value(), TokenCollection( $5, --tmp ).toStr() ) );
    }
;

trace:
    H_PATH opt_cfws COLON opt_cfws path received_seq    %prec CRLF {
 		act_entity->addHeader( new Header( $1->value(), $5.toString() ) );
    }
    | received_seq    %prec CRLF
;

received_seq:
    received                    %prec H_RECEIVED
    | received_seq received     %prec H_RECEIVED
;

received:
    H_RECEIVED opt_cfws COLON opt_cfws 
    name_val_list SEMICOLON opt_cfws date_time { // EOL
		TokenPtr tmp = yy_end;						
 		act_entity->addHeader( 
			new Header( $1->value(), TokenCollection( $5, --tmp ).toStr() ) );
	}
    | H_RECEIVED opt_cfws COLON opt_cfws name_val_list { // EOL // obsolete
		TokenPtr tmp = yy_end;						
 		act_entity->addHeader( 
			new Header( $1->value(), TokenCollection( $5, --tmp ).toStr() ) );
	}
;

name_val_list:
    /* pusty */        %prec ATEXT  {
         $$ = yy_end;
    }
    | name_val_pair
    | name_val_list name_val_pair
;

name_val_pair:
    item_name addr_spec
    | item_name angle_addr_list // msg_id uwzglednione w addr_list
    | item_name domain          // atom uwzgledniony w domain (dot_atom)
;

item_name:
    atom cfws
;

angle_addr_list:
    angle_addr    {
        $$ = AddressCollection( 1, $1 );
    }
    | angle_addr_list angle_addr    {
        $$ = $1;
        $$.push_back( $2 );
    }
;

// Nagłówki MIME ---------------------------------------------------- //

version:
    H_VERSION opt_cfws COLON opt_cfws NUMBER opt_cfws DOT opt_cfws NUMBER opt_cfws
;
    
entity_header:
    content_transfer_encoding
    | content_description
    | content_id
;

// content-type typu nie multipart
content_type:
    H_CONTENT_TYPE opt_cfws COLON opt_cfws TOKEN opt_cfws SLASH TOKEN opt_cfws    {
    //      1        2        3        4     5      6       7     8        9
	act_entity->eContentType = ContentType( $5->value(), $8->value() );
    }
    | H_CONTENT_TYPE opt_cfws COLON opt_cfws TOKEN opt_cfws SLASH TOKEN opt_cfws parameters    {
      //    1            2     3       4       5      6       7     8      9        10
	TokenPtr tmp = yy_end;						
	act_entity->eContentType = ContentType( $5->value(), $8->value(), TokenCollection( $10, --tmp ).toStr() );
    }
    //blad bez slasha
;

multipart_ct:
    H_CONTENT_TYPE opt_cfws COLON opt_cfws MULTIPART opt_cfws SLASH TOKEN opt_cfws    {
    //      1        2        3        4     5      6       7     8        9
	act_entity->eContentType = ContentType( $5->value(), $8->value() );
    }
    | H_CONTENT_TYPE opt_cfws COLON opt_cfws MULTIPART opt_cfws SLASH TOKEN opt_cfws parameters    {
      //    1            2     3       4       5      6       7     8      9        10
	TokenPtr tmp = yy_end;
	act_entity->eContentType = ContentType( $5->value(), $8->value(), TokenCollection( $10, --tmp ).toStr() );
    }
;

parameters:
    SEMICOLON opt_cfws parameter
    | parameters SEMICOLON opt_cfws parameter
;

parameter:
    TOKEN opt_cfws EQUAL opt_cfws TOKEN opt_cfws
    | TOKEN opt_cfws EQUAL opt_cfws quoted_string
    | TOKEN opt_cfws
;

content_transfer_encoding:
    H_TRANSFER_ENC opt_cfws COLON opt_cfws TOKEN opt_cfws    {  /// po H_ nie powinno byc comment
    //    1           2       3     4        5        6
	act_entity->eEncoding =
		boost::iequals( $5->value(), "BASE64" ) ? Base64
		: boost::iequals( $5->value(), "QUOTED-PRINTABLE" ) ? Qp
		: boost::iequals( $5->value(), "BINARY" ) ? Binary
		: boost::iequals( $5->value(), "8BIT" ) ? _8Bit
		: _7Bit;
    }
    | H_TRANSFER_ENC opt_cfws COLON opt_cfws TOKEN opt_cfws parameters    {
      //    1            2      3      4       5       6        7
	act_entity->eEncoding =
		boost::iequals( $5->value(), "BASE64" ) ? Base64
		: boost::iequals( $5->value(), "QUOTED-PRINTABLE" ) ? Qp
		: boost::iequals( $5->value(), "BINARY" ) ? Binary
		: boost::iequals( $5->value(), "8BIT" ) ? _8Bit
		: _7Bit;
    }
;

content_description:
    H_DESCRIPTION opt_cfws COLON opt_cfws unstructured	// unstructured moze == NULL /// po H_ nie powinno byc comment
;

content_id:
    H_CID opt_cfws COLON opt_cfws msg_id    /// po H_ nie powinno byc comment
;

////////////////////////////////////////////////////////////////////////
// Struktura nagłówków
////////////////////////////////////////////////////////////////////////

// Adresy  ---------------------------------------------------------- //

address:
    mailbox {
        $$ = AddressCollection( 1, $1 );
    }
    | group    // moze == NULL
;

address_list:
    /* pusty */    {
        $$ = AddressCollection();
    }
    | alist
    | obs_alist
    | obs_alist alist    %prec CRLF {    // mogloby byc w obs_alist, ale tam powoduje konfliky reduce-reduce
        $$ = $1;
        $$.insert( $$.end(), $2.begin(), $2.end() );
    }
;

alist:
    address
    | alist COMMA opt_cfws address    {
        $$ = $1;
        $$.insert( $$.end(), $4.begin(), $4.end() );
    }
;

obs_alist:
    commas {
        $$ = AddressCollection();
    }
    | alist commas
    | obs_alist alist commas    {
        $$ = $1;
        $$.insert( $$.end(), $2.begin(), $2.end() );
    }
;    

mailbox_list:
    /* pusty */ {
		$$ = AddressCollection();
    }
    | mlist
    | obs_mlist
    | obs_mlist mlist    {    // mogloby byc w obs_mlist, ale tam powoduje konfliky reduce-reduce
        $$ = $1;
        $$.insert( $$.end(), $2.begin(), $2.end() );
    }
;

mlist:
    mailbox    %prec CRLF   {
        $$ = AddressCollection( 1, $1 );
    }
    | mlist COMMA opt_cfws mailbox    {
        $$ = $1;
        $$.push_back( $4 );
    }
;

obs_mlist:
    commas    {
        $$ = AddressCollection();
    }
    | mlist commas
    | obs_mlist mlist commas    {
        $$ = $1;
        $$.insert( $$.end(), $2.begin(), $2.end() );
    }
;    
    
mailbox:
    addr_spec
    | name_addr
    | quoted_string {
        
        std::list<int> ommit_tokens;
        ommit_tokens.push_back( yy::BisonParser::token::LDQUOTE );
        ommit_tokens.push_back( yy::BisonParser::token::RDQUOTE );
        
	TokenPtr tmp = yy_end;						
        std::string domain, localpart = TokenCollection( $1, --tmp ).toStr( ommit_tokens );   // bez ""

        size_t at = localpart.find_first_of('@');
        if( at == std::string::npos || at == 0 
            || at == localpart.size() - 1 )
		{
            $$ = Address();
		}
        else
        {
            domain = localpart.substr( at + 1, localpart.size() - 1 );
            localpart.erase( at, localpart.size() - at );
            
            $$ = Address( localpart, domain );
        }
    }
;

group:
    phrase COLON opt_cfws mailbox_list SEMICOLON opt_cfws    {
    // 1    2       3       4           5           6
        $$ = $4;
    }
;

// Pojedyncze adresy ------------------------------------------------ //

name_addr:
    angle_addr
    | phrase angle_addr {
        $$ = $2;
    }
;

// obs-angle-addr = [CFWS] "<" [obs-route] addr-spec ">" [CFWS]
angle_addr:
    LTRB opt_cfws obs_route addr_spec RTRB opt_cfws    {
        $$ = $4;        
    }
;

obs_route:
    /* pusty */    %prec CRLF   {
        $$ = yy_end;
    }
    | obs_domain_list COLON opt_cfws
;

// obs-domain-list = "@" domain *(*(CFWS / "," ) [CFWS] "@" domain)
obs_domain_list:
    obs_domain_name
    | obs_domain_list obs_domain_name
;

// "@" domain
obs_domain_name:
    AT opt_cfws domain opt_commas
;

// Addr-spec -------------------------------------------------------- //

addr_spec:
    localpart AT opt_cfws domain    {
    
        std::list<int> ommit_tokens;
        ommit_tokens.push_back( yy::BisonParser::token::LDQUOTE );
        ommit_tokens.push_back( yy::BisonParser::token::RDQUOTE );
        ommit_tokens.push_back( yy::BisonParser::token::RSQB);
        ommit_tokens.push_back( yy::BisonParser::token::WSP);
        ommit_tokens.push_back( yy::BisonParser::token::CRLF);
        
        std::string l = TokenCollection( $1, $2 ).toStr( ommit_tokens ); 
        std::string d = TokenCollection( $4, yy_last ).toStr( ommit_tokens ); 
           
        $$ = Address( l, d );
    }    // | pusty - bład 
;

localpart:
    dot_atom
    | quoted_string
;

domain:
    dot_atom
    | domain_literal
;

// rfc822:  [10.0.3.19]
// rfc2822: literal Internet address of the particular host
domain_literal:
    LSQB opt_fws dcontent_seq RSQB opt_cfws    {
		$$ = $3;
    }
;

// *([FWS] dcontent) [FWS]
dcontent_seq:
    dcontent opt_fws
    | dcontent_seq dcontent opt_fws
;

dcontent:
    dtextseq    %prec DTEXT
    | QUOTED_PAIR
;

dtextseq:
    ATEXT
    | DTEXT
    | dtextseq ATEXT
    | dtextseq DTEXT
;

// Message ID i path ------------------------------------------------ //

// 1*msg-id | *(phrase | msg-id)
msg_id_list:
    /* pusty */             %prec CRLF {
        $$ = AddressCollection();
    }
    | msg_list              %prec CRLF
    | obs_msg_list          %prec CRLF
    | msg_list obs_msg_list %prec CRLF   {
        $$ = $1;
        $$.insert( $$.end(), $2.begin(), $2.end() );
    }
;
    
// 1*msg-id
msg_list:
    msg_id    {        
        $$ = AddressCollection( 1, $1 );
    }
    | msg_list msg_id    {
        $$ = $1;
        $$.push_back( $2 );
    }
;

// phrase | msg-id // (obsolete)
obs_msg_list:
    phrase    %prec CRLF {
        $$ = AddressCollection();
    }
    | obs_msg_list phrase       %prec CRLF     {
        $$ = $1;
    }
    | obs_msg_list msg_list     %prec CRLF   {
        $$ = $1;
        $$.insert( $$.end(), $2.begin(), $2.end() );
    }
;

msg_id:
    angle_addr   // uproszczenie: w msg-id nie ma fws ani cfws
;

path:
    angle_addr
    | LTRB opt_cfws RTRB opt_cfws   {
        $$ = Address();
    }
;

// Daty i czas  ----------------------------------------------------- //

date_time:
	DAY opt_cfws COMMA opt_cfws			// day_name, 
	NUMBER cfws MONTH cfws NUMBER cfws	// day month year
	time zone
	| NUMBER cfws MONTH cfws NUMBER cfws time zone
;

time:
	NUMBER COLON opt_cfws NUMBER opt_cfws
	| NUMBER COLON opt_cfws NUMBER opt_cfws COLON NUMBER cfws
;

zone:
    SIGN NUMBER opt_cfws
    | OBS_ZONE opt_cfws
;

// Unstructured  ---------------------------------------------------- //

unstructured:
	/* pusty */		%prec CRLF	{
        $$ = yy_end;
	}
    | unstructured_elem opt_cfws
    | unstructured unstructured_elem opt_cfws
;

unstructured_elem:
    UNSTRUCTURED
    | ENCODED_WORD
;

////////////////////////////////////////////////////////////////////////
// Ciało wiadomości
////////////////////////////////////////////////////////////////////////

singlepart_body:
    textseq opt_crlfs		%prec CRLF {
	if( $1 != yy_end )
	{
		TokenPtr tmp = yy_end;
		if( $1 != --tmp )
			act_entity->eData = TokenCollection( $1, tmp ).toStr();
	}
	#ifdef DEBUG
	std::cout << "<- Singlepart: " << act_entity->eData << std::endl;
	#endif // DEBUG
    }
;

multipart_body:
	textseq opt_crlfs delimiter_entity
	| multipart_body delimiter_parts close_delimiter textseq opt_crlfs 
	| multipart 
	| multipart_body multipart 
;

delimiter_parts:
	entity
	| delimiter_entity
	| delimiter_parts delimiter_entity
	| delimiter_parts entity
;

delimiter_entity:
	delimiter 
	{ 
		Entity *parent = act_entity;
		act_entity = new Entity();
		parent->addChild( act_entity );
		#ifdef DEBUG
		std::cout << "-> Delim entity of delim: " << $1->value() << std::endl << std::flush << std::endl;
		#endif // DEBUG
	}
	entity {
		if( act_entity->getParent() == NULL )
			YYABORT;
		act_entity = act_entity->getParent ();

		#ifdef DEBUG
		std::cout << "<- Delim entity " << std::endl;
		#endif // DEBUG
	}
;

delimiter:
    BOUNDARY crlf
    | BOUNDARY wsp crlf
;

close_delimiter:
    BOUNDARY_END opt_cfws {
	#ifdef DEBUG
	std::cout << "close delim: " << $1->value() << std::endl << std::flush << std::endl;
	#endif // DEBUG
}
    | BOUNDARY_END wsp opt_cfws {
	#ifdef DEBUG
	std::cout << "close delim: " << $1->value() << std::endl << std::flush << std::endl;
	#endif // DEBUG
}
;

textseq:
    /* pusty */		%prec TEXT {
	$$ = yy_end;
    }
    | text              %prec TEXT	
    | textseq text      %prec TEXT
;

// body z rfc2822
text:
    TEXT
    | text crlf
    | text wsp %prec WSP
;

////////////////////////////////////////////////////////////////////////
// Podstawowe elementy
////////////////////////////////////////////////////////////////////////

// Komentarze ------------------------------------------------------- //

comment:
    LB opt_fws ccontent_seq RB opt_cfws
;

ccontent_seq:
    ccontent opt_fws
    | ccontent_seq ccontent opt_fws
;

ccontent:
    ctextseq        %prec CTEXT
    | QUOTED_PAIR    %prec QUOTED_PAIR
    | comment 
    | ENCODED_WORD
;

ctextseq:
    CTEXT
    | ctextseq CTEXT
;

// Cytaty ----------------------------------------------------------- //

quoted_string:
	LDQUOTE qcontent_seq RDQUOTE opt_cfws
	| LDQUOTE fws qcontent_seq RDQUOTE opt_cfws
	| LDQUOTE cfws qcontent_seq RDQUOTE opt_cfws
;

qcontent_seq:
    qcontent opt_fws
    | qcontent_seq qcontent opt_fws
;

qcontent:
    QUOTED_PAIR
    | qtextseq      %prec QTEXT
;

qtextseq:
    QTEXT
    | qtextseq QTEXT
;

// Atomy  ----------------------------------------------------------- //

phrase_list:
    phrase              %prec CRLF
    | phrase_list COMMA opt_cfws phrase
    | obs_phrase_list   %prec CRLF
;

// phrase_list ,,
// ,,
// ,, phrase_list
// phrase ,, phrase_list
obs_phrase_list:
    commas    %prec CRLF 
    | phrase_list commas    %prec CRLF    
    | obs_phrase_list phrase_list    %prec CRLF
;

phrase:    // 1 słowo
    quoted_string
    | dot_atom        %prec DOT
    | ENCODED_WORD opt_cfws
    | phrase quoted_string
    | phrase ENCODED_WORD opt_cfws
    | phrase dot_atom    %prec DOT 
    | phrase dot_atom phrase    %prec CRLF
;

dot_atom:
    atom opt_cfws 		 // w dot-atom nie ma cfws (dla zgodności z rfc2822)
    | dots    		%prec CRLF  // blad
    | dot_atom DOT opt_cfws atom opt_cfws
    | dot_atom dots atom opt_cfws    // błąd !
;

atom:
    atextseq     	%prec ATEXT
;

atextseq:
	ATEXT
	| NUMBER		// NUMBER tutaj ze wzgl na implementacje skanera
	| atextseq ATEXT
	| atextseq NUMBER
;

// Core i Primitives ------------------------------------------------ //

opt_cfws:
    /* pusty */     %prec CRLF    {
        $$ = yy_end;
    }
    | cfws          %prec CRLF
;

cfws:
    fws				%prec CRLF
    | comment
    | cfws comment
    | cfws fws		%prec CRLF
;

/* FWS = ([*WSP CRLF] 1*WSP) / obs-FWS */
opt_fws:
    /* pusty */     %prec CRLF    {
        $$ = yy_end;
    }
    | fws           %prec CRLF
;

fws:
	wsp				%prec WSP
	| crlf          %prec CRLF
	| fws wsp		%prec WSP
	| fws crlf      %prec CRLF
;
wsp:
    WSP
    | wsp WSP
;

crlf:
    CRLF
;
    
opt_crlfs:
    /* pusty */     %prec CRLF    {
        $$ = yy_end;
    }
    | crlf
    | opt_crlfs crlf
;

commas:
    COMMA opt_cfws COMMA opt_cfws
    | commas COMMA opt_cfws
;

opt_commas:
    /* pusty */		%prec CRLF    {
        $$ = yy_end;
    }
    | COMMA opt_cfws
    | commas    	%prec CRLF
;

dots:
    DOT opt_cfws
    | dots DOT opt_cfws
;

// ------------------------------------------------------------------ //

%%

/* Definicja niezdefiniowanych metod bisona */

void yy::BisonParser::error( const yy::location &loc, 
                             const std::string &err )
{
	std::cout << std::endl << std::endl << "PARSER - BLAD: " << err;
	scanner->error();
}
